/* Connection.java
 * 
 * Establishes and maintains the client's connetion to the server object.
 * 
 * coded by Darren Woodley and Brandon McKune */

import java.io.*;
import java.net.*;
import java.net.UnknownHostException;
import java.sql.Time;
import java.util.Calendar;
import java.util.Date;
import java.rmi.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.lang.System;
import java.util.*;

public class Connection extends Thread implements Runnable
{

	private InetAddress inet = null;
	private String nickname;
	private String localHostName;
	private String serverName;
	private String loginURL;
	private String chatURL;
	private final int RMI_PORT = 1099;
	private final int CHAT_PORT = 21212;
	private String [] players = null;
	private String DEFAULT_IP = "127.0.0.1";
	private String DEFAULT_NAME = "Default";
	private String CHAT_NOT_SENT = "Server non-repsonding -> IP:";
	private String MCAST_GROUP = "224.0.0.1";
	private String time = null;
	private boolean connected;
	
	private BShipServerInterface bship;
	private String message = null;
	private boolean newMessage = false;
	private MulticastSocket chatSocket;
	private ChatClientThread chat;

	Socket server = null;

	private static Connection conn = null;


	private Connection()
	{
		try
		{
			connected = true; // thinking this should actually be false
			inet = InetAddress.getByName(DEFAULT_IP);
			chat = new ChatClientThread(CHAT_PORT);
			chatSocket = new MulticastSocket();
			chatSocket.joinGroup(InetAddress.getByName(MCAST_GROUP));
		}
		catch(UnknownHostException uhe)
		{
			uhe.printStackTrace();
		}
		catch(IOException ioe)
		{
			System.out.println("IOException in Connection:");
			ioe.printStackTrace();
		}
		nickname = DEFAULT_NAME;
	}

	public static synchronized Connection getInstance()
	{
		if(conn == null)
		{
			conn = new Connection();
		}
		return conn;
	}
	
	public boolean doAddShip( char shipType, char alignment, int x, int y ){
		try{
				bship.placeShip(nickname, shipType, alignment, x, y);
				return true;
			}
		catch(RemoteException re) 
			{
				System.out.println( "Exception found in Connection.doAddShip().");
				re.printStackTrace();
				return false;
			}		
	}
	
	public boolean doFireShot( String playerName, int x, int y, int result ){
		try{
				bship.fire(playerName, x, y, result);
				return true;
			}
		catch(RemoteException re) 
			{
				System.out.println( "Exception found in Connection.doFireShot().");
				re.printStackTrace();
				return false;
			}		
	}

	
	// sets the RMI remote IP address
	public boolean setServer(String name)
	{
		serverName = name;
		System.out.println("Server IP set to: " + serverName);
		return true;
	}
	
	// sets the client user
	public boolean setUser(String name)
	{
		nickname = name;
		return true;
	}
	// logs into server and starts chat session
	public boolean connect()
	{
		System.setProperty("java.security.policy", "server.policy");
	    if(System.getSecurityManager() == null)
		        System.setSecurityManager(new SecurityManager());
		try
		{
		    String name = "bship";
			Registry registry = LocateRegistry.getRegistry(serverName);
			String[] list = registry.list();
			for(String str : list)
				System.out.println(str);
			bship = (BShipServerInterface)registry.lookup(name);
			localHostName = InetAddress.getLocalHost().getHostName();
			System.out.println(nickname + " requesting login @" + localHostName);
			boolean success = bship.login(nickname, getIP());
			if(success)
	        {
			connected = true;
			ClientController.getInstance(ClientFrame.getInstance()).printToInteraction("login successful", "");
	        	chat.start();
	        }
			return success;
		}
        catch(Exception e)
        {
			e.printStackTrace();	
        	return false;
        }       
	}
	
	

	// not sure why this exists or if it is actually used anywhere, makes 
	// more sense to have connect / endConnection set the value
	public void run()
	{
		connected = true;
	}

	// DEPRECATED, originally intended to return logged in players
	public String [] dumby()
	{
		players = new String[6];
		for(int i = 0; i < players.length; i++)
		{
			players[i] = "Brandon" + i;
		}
		return players;
	}

	public boolean isConnected()
	{
		return connected;
	}

	public void endConnection()
	{
		try
		{
			boolean success = bship.logout(nickname);
			if(success)
		    {
			    connected = false;
			    conn = null;
			    server = null;
	        }
		}
		catch(RemoteException re)
		{
		}
	}
	
	// returns list of all currently logged in players,
	// ClientController should be calling this, not dumby()
	public Vector<String> getPlayers()
	{
		Vector<String> temp = null;
		
		try
		{	
			temp = bship.getPlayers();
		}
		catch(RemoteException re)
		{
			System.out.println("RemoteException found in Connection.getPlayers: ");
			re.printStackTrace();
		}
		
		return temp;

	}
	
	public void sendChat(String message)
	{
		try
		{
			// send message, check for confirmation of broadcast
			String str = nickname + ": " + message;
			boolean success = bship.sendMessage(nickname, message);
			
			// if not successfully broadcast, send error to Interaction pane
			if(!success)
        	{
				CHAT_NOT_SENT += localHostName;
				ClientController.getInstance(ClientFrame.getInstance()).printToInteraction(CHAT_NOT_SENT, getTime());
        	}
		}
		catch(RemoteException re)
		{
			System.out.println("RemoteException in Connection.sendChat: ");
			re.printStackTrace();
		}
		catch(IOException ioe)
		{
			System.out.println("IOException in Connection.sendChat: ");
			ioe.printStackTrace();
		}
	}
	
	public String getTime(){
		Date temp = new Date();
	
		time = temp.toString();
		int index = time.indexOf(":");
		time = time.substring((index-2), (index +6));
		
		System.out.println(time);
		
		return time;
	}
	
	public String getIP()
	{
		InetAddress inet = null;
		String str = "";
		int temp;
		try
		{
			inet = InetAddress.getLocalHost();
		}
		catch(IOException ioe)
		{
			System.err.println("Localhost not found");
		}
		byte[] add = inet.getAddress();
		temp = (add[0] >= 0)? add[0]:((add[0]+ 256) % 256);
		str += temp;
		for(int i = 1; i < add.length; i++)
		{
			temp = (add[i] >= 0)? add[i]:((add[i] + 256) % 256);
			str += ("." + temp); 
		}
		return str;
	}
	
	public String getNickName(){
		return nickname;
	}


	// socket listening thread that receives multicast messages sent to
	// the chat port
	private class ChatClientThread extends Thread
	{  
		int chatPort;
	
		public ChatClientThread(int port)
		{
			super();
			chatPort = port;
			System.out.println("ChatClientThread created");
		}
    	public void run()
    	{
	    	InetAddress group; // multicast group address
        	MulticastSocket socket;
        
	        try
        	{
            	group = InetAddress.getByName("224.0.0.1");
				System.out.println("Creating socket");
	            socket = new MulticastSocket(port);
            	socket.joinGroup(group);
            	byte[] buf = new byte[32];
            	DatagramPacket dp = new DatagramPacket(buf, buf.length);
				System.out.println("Listening for messages");
            	while(connected)
        	    {
					 buf = new byte[32]; 
					 dp = new DatagramPacket(buf, buf.length);
					 socket.receive(dp);
            	     String message = new String(dp.getData());
					 ClientController.getInstance(ClientFrame.getInstance()).printToChat(message, Connection.getInstance().getTime());
            	}
        	}
        	catch(UnknownHostException uhe)
        	{
	            System.out.println("UHException in ChatClientThread.run:");
				uhe.printStackTrace();
        	}
        	catch(IOException ioe)
        	{
	            System.out.println("IOException in ChatClientThread.run:");
				ioe.printStackTrace();
        	}
    	}
	}
}
